import G from "./Utilities/Global";
import Constants from "./Constants/Constants";
import EditLayer from "./UI/EditLayer";
import C from "./Configs/Config";
import MapRender from "./Map/MapRender";
import EventDefines from "./Constants/EventDefines";
import EntityTypes from "./Constants/EntityTypes";
import MonsterController from "./Controllers/MonsterController";
import Monster from "./Entity/Monster";
import MainPlayer from "./Entity/MainPlayer";
import Effects from "./Constants/Effects";
import FlyingKey from "./Components/Key";
import eWeaponState from "./Constants/WeaponState";
import MapData from "./Map/MapData";
import IAsset from "./Interfaces/IAsset";
import EntityStruct from "./Entity/EntityStruct";
import MapAssetStruct from "./Structs/MapAssetStruct";
import CastType from "./Constants/CastType";
import EntityGroup from "./Constants/EntityGroup";
import Bullet from "./Entity/Bullet";
import UIGameEnd from "./UI/UIGameEnd";

/**
 * 战斗场景 
 * @createtime 2018年10月29日11:18:51
 * @created by five
 */

const { ccclass, property } = cc._decorator;

@ccclass
export default class BattleScene extends cc.Component {
    /**编辑框基点 */
    @property(cc.Node)
    editAttachPoint: cc.Node = null;
    /**所有墙的挂载点 */
    @property(cc.Node)
    mapAttachPoint: cc.Node = null;
    /**分数 */
    @property(cc.Label)
    score: cc.Label = null;
    /**信息层节点 */
    @property(cc.Node)
    infoNode: cc.Node = null;
    /**剩余时间 */
    @property(cc.Label)
    time: cc.Label = null;
    /**Entity挂载点 */
    @property(cc.Node)
    EntityAttachPoint: cc.Node = null;
    /**背景图  */
    @property(cc.Node)
    Background: cc.Node = null;
    @property(cc.Node)
    EditButton: cc.Node = null;
    @property(cc.Node)
    PauseButton: cc.Node = null;
    // 地图信息
    public MapData: MapData = null;
    // 地图渲染器
    public MapRender: MapRender = null;
    // 怪物管理器
    mController: MonsterController;
    @property(cc.EditBox)
    jumpForceTest: cc.EditBox = null;
    @property(cc.EditBox)
    moveSpeedTest: cc.EditBox = null;
    @property(cc.Node)
    moveLeft: cc.Node = null;
    @property(cc.Node)
    moveRight: cc.Node = null;

    timeLeft: number = 0;
    // 当前地图的index
    curMapIndex: number;

    start() {
        // 注册运行类
        G().CurScene = this;

        this.PauseButton.runAction(cc.fadeOut(1));
        this.node.on(cc.Node.EventType.TOUCH_START, () => {
            this.PauseButton.runAction(cc.fadeIn(1));
            setTimeout(() => {
                if (this.PauseButton)
                    this.PauseButton.runAction(cc.fadeOut(1));
            }, 3000);
        });
    }

    timeSchedule() {
        if (this.timeLeft <= 0) {
            this.GameEnd(false);
            return;
        }
        this.timeLeft--;
        this.time.string = this.timeLeft + "";
    }

    /**
     * 加载地图配置
     * @param mapIndex 地图序号
     * @param isedit 是否编辑模式
     */
    public async LoadMap(mapIndex: number, isEdit: boolean = false) {
        // 没有下一关了
        if (mapIndex == -1 && !isEdit)
            return;

        this.Initialize(mapIndex, isEdit);
        this.excuteEvents();
        this.infoNode.active = !isEdit;
    }

    public async Initialize(index: number, isEdit: boolean) {
        this.EntityAttachPoint.removeAllChildren();
        this.mapAttachPoint.removeAllChildren();
        let mData = C().Map.GetMapByIndex(index);
        this.curMapIndex = index;
        this.SetRunningData(mData, isEdit);
        this.EditButton.active = isEdit;
    }

    public Restart() {
        this.Initialize(this.curMapIndex, false);
    }

    public async Next() {
        this.curMapIndex = C().Map.GetNext(this.curMapIndex);
        G().ShowSplash(this.curMapIndex);
        this.Initialize(this.curMapIndex, false);
    }

    public JumpTo(mapName: string) {
        let index = C().Map.GetIndexByName(mapName);
        this.Initialize(index, true);
    }

    private async loadPlayer() {
        let node = await G().Res.GetRes(Constants.PRE_PLAYER) as cc.Node;
        G().MainPlayer = node.getComponent(MainPlayer);
        this.EntityAttachPoint.addChild(node);
        let es = this.MapData.GetEntityByType(EntityTypes.BORNPOINT);
        node.position = G().Calc.LocationToPostion(es.Location);
    }

    private async loadMonster() {
        if (this.mController)
            this.mController.Init()
        else
            this.mController = this.node.addComponent(MonsterController);
    }

    /**
     * @param d 正在使用的地图信息
     */
    public SetRunningData(d: MapData, isEdit: boolean) {
        this.MapData = new MapData(d, isEdit);
        let node = new cc.Node();
        this.MapRender = node.addComponent(MapRender);
        this.MapRender.SetData(this.MapData, isEdit);
        this.mapAttachPoint.addChild(node);
        G().Calc.RunningMapSize(this.MapData.Size);
        G().AStar.Init(this.MapData);
        G().Sound.PlayBGM("game_bgm");
        if (this._editLayer)
            this._editLayer.getComponent(EditLayer).SetMapData(this.MapData);
        this.SetBackground(this.MapData.Background);
        if (isEdit) return;
        this.timeLeft = !isNaN(d.TimeLimit) ? d.TimeLimit : 0;
        this.time.string = this.timeLeft + "";
        this.schedule(this.timeSchedule, 1);
        this.LoadObjects();
        this.addMoveListener();
    }

    public async SetBackground(bgName: string) {
        this.MapData.Background = bgName;
        let bgFrame = await G().Res.GetSpriteFrame("bg/" + bgName) as cc.SpriteFrame;
        this.Background.getComponent(cc.Sprite).spriteFrame = bgFrame;
    }

    public LoadObjects() {
        this.loadPlayer();
        this.loadMonster();
    }

    private _editLayer: cc.Node = null;

    /**
     * 打开编辑界面
     */
    public async btnEditClick() {
        if (this._editLayer == null) {
            this._editLayer = await G().Res.GetRes(Constants.PRE_EDITLAYER) as cc.Node;
            this._editLayer.getComponent(EditLayer).SetMapData(this.MapData);
            this.editAttachPoint.addChild(this._editLayer);
        } else {
            this.editAttachPoint.removeChild(this._editLayer);
            this._editLayer = null;
        }
    }

    public AddTile(loc: number, asset: IAsset) {
        if (asset == null) {
            this.MapRender.RemoveTile(loc);
            return false;
        }
        return this.MapRender.AddTile(loc, asset);
    }

    public RemoveTiles(loc: number) {
        this.MapRender.RemoveTiles(loc);
    }

    public BoomTile(position: cc.Vec2, destory: boolean = false): CastType {
        if (!destory && this.mController.ExistMonster(position))
            return CastType.FAILED;

        let drop = this.MapRender.BoomTile(position, destory);
        if (drop[0] > 0) {
            let asset = C().MapAsset.GetAsset(drop[0]);
            this.MapRender.AddTile(drop[1], asset);
        }
        return drop[2];
    }

    // 上次撞的地方,防止重复碰撞
    private lastHead: number = -1;
    excuteEvents() {
        // 编辑 添加地块
        this.node.on(EventDefines.MAPEDIT_ADD, (event: cc.Event.EventCustom) => {
            let data = event.detail;
            this.MapRender.AddTile(data[0], data[1]);
        }, this);

        // 编辑 删除地块
        this.node.on(EventDefines.MAPEDIT_REMOVE, (event: cc.Event.EventCustom) => {
            this.MapRender.RemoveTiles(event.detail);
        }, this);

        // 游戏中, 添加删除地块
        this.node.on(EventDefines.PLAY_TILEEDIT, (event: cc.Event.EventCustom) => {
            let result = this.BoomTile(event.detail);
            if (result == CastType.DESTORY)
                G().Sound.playSFX("road_destory");
            else if (result == CastType.MAKE)
                G().Sound.playSFX("road_make");
            else
                G().Sound.playSFX("road_failed");
        }, this);

        // 子弹碰撞
        this.node.on(EventDefines.EVENT_BULLET_CONTACT, (event: cc.Event.EventCustom) => {
            let sender = event.detail[0] as Bullet;
            let owner = sender.GetOwner();
            let other = event.detail[1];
            let position = other.node.position;
            let entity = this.MapData.GetWallByPosition(position);
            // 判断是谁发出来的子弹
            if (owner.Group == EntityGroup.PLAYER) {
                cc.log('player shot ' + position);
            } else if (owner.Group == EntityGroup.MONSTER) {
                // cc.log('monster shot ' + position);
                let player = other.node.getComponent(MainPlayer);
                if (player)
                    this.GameEnd(false);
                else if (entity) {
                    this.BoomTile(position, true);
                    sender.recollect();
                }
            }
        }, this);

        // 铁头功,顶墙
        this.node.on(EventDefines.EVENT_IRONHEAD, (event: cc.Event.EventCustom) => {
            let node = event.detail.node;
            let loc = G().Calc.PositionToLocation(node.position);
            // 妈的。怪物有可能来撞我的头
            let entity = this.MapData.GetWallByPosition(node.position);
            if (entity && loc != this.lastHead) {
                let drop = this.MapRender.BoomTile(node.position);
                if (entity.Asset.id == Constants.WALL_ASSETID) {
                    let asset = C().MapAsset.GetAsset(Constants.BWALL_ASSETID);
                    if (drop[0] > 0)
                        asset.enumber = drop[0];
                    this.MapRender.AddTile(loc, asset);
                    this.lastHead = loc;
                } else if (drop[0] > 0) {
                    let asset = C().MapAsset.GetAsset(drop[0]);
                    this.MapRender.AddTile(drop[1], asset);
                    this.lastHead = loc;
                }

                this.scheduleOnce(() => { this.lastHead = -1 }, 0.2);
                G().Sound.playSFX("ironhead");
            }
        }, this);

        // 玩家碰撞到物体
        this.node.on(EventDefines.EVENT_PLAYER_CONTACT, (event: cc.Event.EventCustom) => {
            // 上次碰撞还没完成
            if (this.lastHead > 0)
                return;

            let sender = event.detail[0] as MainPlayer;
            let receiver = event.detail[1];
            let entity = this.MapData.GetEntityByPosition(receiver.node.position, true);
            if (entity && C().IsTypeOf(entity.Asset, EntityTypes.COLLECT)) {
                let drop = entity.Attach(sender);
                if (drop[0] > 0) {
                    // 效果
                    let effect = drop[1];
                    // 数量
                    let number = drop[2];
                    this.dealEffect(effect, number, entity);
                }
            }
            else {
                let monster = receiver.node.getComponent(Monster);
                // 撞到怪物了，扑街
                if (monster) {
                    G().MainPlayer.die();
                    this.GameEnd(false);
                }
            }
        }, this);
    }

    private eranscore: number = 0;
    updateScore(n: number) {
        this.eranscore += n;
    }

    dealEffect(e: Effects, n: number, struct: EntityStruct) {
        let asset = struct.Asset as MapAssetStruct;
        G().Sound.playSFX(asset.sound);

        switch (e) {
            case Effects.OPENDOOR:
                this.openDoor(struct.Location);
                return;
            case Effects.STRAIGHT:
                cc.log("直线火球");
                G().MainPlayer.SetWeapon(eWeaponState.STRIGHTFIRE);
                break;
            case Effects.PASTEWALL:
                cc.log("寻路火球");
                G().MainPlayer.SetWeapon(eWeaponState.PASTEFIRE);
                break;
            case Effects.SCORE:
                cc.log("加分 = ", n);
                this.updateScore(n);
                break;
            case Effects.GAMEWIN:
                this.GameEnd(true);
                return;
            case Effects.KILLALL:
                this.mController.KillAll();
                break;
            case Effects.ADDTIME:
                this.timeLeft += n;
                break;
            case Effects.FREEZE:
                this.mController.TimeFreeze(n);
                break;
        }
        this.MapRender.RemoveTile(struct.Location);
    }

    /**
     * 开门
    //  * @param loc 钥匙的位置,懒得自己去算了,在哪撞到就在哪拿
     */
    openDoor(loc: number) {
        let node = this.MapRender.GetTile(loc);
        let door = this.MapRender.GetDoor();
        let fkey = node.addComponent(FlyingKey);
        node.removeComponent(cc.PhysicsBoxCollider);
        node.removeComponent(cc.RigidBody);
        fkey.SetTarget(door[1], () => {
            this.MapRender.RemoveTile(loc);
            this.MapRender.AddTile(door[0], C().MapAsset.GetAsset(Constants.ASSET_ODOOR));
            G().Sound.playSFX("door_open");
        });
    }

    PauseClicked() {
        G().Setting.Open(0);
    }

    async GameEnd(win: boolean) {
        let node = await G().Res.GetRes(Constants.PRE_GAMEEND) as cc.Node;
        let script = node.getComponent(UIGameEnd);
        this.node.addChild(node);
        script.Gameend(win);
        G().Sound.PlayBGM(win ? "game_victory" : "game_failed", false);
    }

    MakeOrDestory() {
        G().MainPlayer.Attack();
    }

    JumpClicked() {
        G().MainPlayer.jump();
    }

    SqautMake() {
        G().MainPlayer.sqautMake();
    }

    addMoveListener() {
        let leftbox = this.moveLeft.getBoundingBox()
        let rightbox = this.moveRight.getBoundingBox()
        this.node.on(cc.Node.EventType.TOUCH_START, (event: cc.Event.EventTouch) => {
            let dpos = this.infoNode.convertToNodeSpace(event.touch.getLocation());
            //@ts-ignore
            if (leftbox.contains(dpos)) {
                this.changeButton(this.moveLeft, "move_left_l");
                this.changeButton(this.moveRight, "move_right");
                G().MainPlayer.move(true);
            }
            //@ts-ignore
            if (rightbox.contains(dpos)) {
                this.changeButton(this.moveRight, "move_right_l");
                this.changeButton(this.moveLeft, "move_left");
                G().MainPlayer.move(false);
            }
        }, this);
        this.node.on(cc.Node.EventType.TOUCH_CANCEL, (event: cc.Event.EventTouch) => {
            G().MainPlayer.stop();
        }, this);
        this.node.on(cc.Node.EventType.TOUCH_END, (event: cc.Event.EventTouch) => {
            G().MainPlayer.stop();
        }, this);
        this.node.on(cc.Node.EventType.TOUCH_MOVE, (event: cc.Event.EventTouch) => {
            let dpos = this.infoNode.convertToNodeSpace(event.touch.getLocation());
            //@ts-ignore
            if (leftbox.contains(dpos)) {
                this.changeButton(this.moveLeft, "move_left_l");
                this.changeButton(this.moveRight, "move_right");
                G().MainPlayer.move(true);
            }
            //@ts-ignore
            if (rightbox.contains(dpos)) {
                this.changeButton(this.moveRight, "move_right_l");
                this.changeButton(this.moveLeft, "move_left");
                G().MainPlayer.move(false);
            }
        }, this);
    }

    async changeButton(button: cc.Node, spName: string) {
        let sprite = button.getComponent(cc.Sprite);
        let spFrame = await G().Res.GetSpriteFrame(spName) as cc.SpriteFrame;
        sprite.spriteFrame = spFrame;
    }
}
